package controllers

import (
	"encoding/json"
	"github.com/pkg/errors"
	"github.com/spf13/cast"
	"nft-server/common"
	"nft-server/errcode"
	"nft-server/logger"
	"nft-server/model"
	"nft-server/service"
	"nft-server/util"
	"regexp"
	"time"
)

//资产集合表管理

const (
	AddCollectionURI     = "/v1/collection/add"
	QueryCollectionURI   = "/v1/collection/query"
	ModifyCollectionURI  = "/v1/collection/modify"
	PublishCollectionURI = "/v1/collection/publish"

	// GetCollectionNameForMintURI 获取管理员创建的(可见)集合
	GetCollectionNameForMintURI = "/v1/collection/names/invisible"

	// GetCollectionForHomePageURI 获取用于首页展示的集合信息
	GetCollectionForHomePageURI = "/v1/collection/detail"

	GetCollectionForRecommend = "/v1/collection/recommend"

	GetCollectionForPublish = "/v1/collection/publish/detail"
)

type CollectionDetailController struct {
	CommonController
}

func (nc *CollectionDetailController) CheckParameterForAddCollectionDetail(req common.CollectionReq) error {
	//名称长度校验+重名检测
	collections, err := service.CheckCollectionName(req.CollectionName)
	if err != nil {
		logger.Error(err.Error())
		return err
	}
	if len(collections) > 0 {
		err = errors.Errorf("集合名已被使用")
		return err
	}
	if err = nc.CheckUserIdFromSession(req.UserId); err != nil {
		err = errors.Errorf("无效用户ID")
		return err
	}
	return nil
}

// AddCollectionDetail 创建资产集合信息表
func (nc *CollectionDetailController) AddCollectionDetail() {
	//请求头
	req := common.CollectionReq{}
	if err := json.Unmarshal(nc.Ctx.Input.RequestBody, &req); err != nil {
		logger.Error("AddCollectionReq Unmarshal error: %v", err)
		nc.HandleErrCode(errcode.E.Base.RequestInvalid.WithErr(err))
		return
	}
	err := nc.CheckParameterForAddCollectionDetail(req)
	if err != nil {
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithErr(err))
		return
	}
	userId := req.UserId
	//默认处于未铸造状态
	req.CollectionStatus = util.CollectionUnMint
	//不断尝试uuid生成，直到链上铸造collectionIndex成功
	var collectionIndex string
	for true {
		collectionIndex = util.GetCollectionIndexFromUUID()
		//集合ID校验
		if isMatch, err := regexp.MatchString(collectionRegex, collectionIndex); !isMatch || err != nil {
			logger.Error("invalid collection name [%v]", collectionIndex)
			continue
		}
		//判断是否已创建过该集合ID, err == nil说明表中存在该记录，直接返回重复创建
		_, err := service.GetCollectionDetailByCollection(collectionIndex, util.DefaultSubCollectionIndex)
		if err == nil {
			logger.Error("The collection already exists. (collectionIndex: %v)", collectionIndex)
			continue
		}
		//集合铸造到链上
		_, err = service.CreateNFTCollection(collectionIndex)
		if err != nil {
			logger.Error("CreateNFTCollection error: %v", err)
			continue
		}
		break
	}
	//增加资产集合
	collection, err := service.CreateCollectionDetail(userId, collectionIndex, req)
	if len(collection.CollectionName) != 0 && len(collection.CollectionIndex) != 0 {
		_, err = service.SetCollectionName(collection.CollectionIndex, collection.CollectionName, util.DefaultCollectionSymbol)
		if err != nil {
			logger.Error("SetCollectionName error: %v, userId: %v", err, userId)
			nc.HandleErrCode(errcode.E.Collection.Create.WithErr(err))
			return
		}
	}
	if err != nil {
		logger.Error("CreateCollectionDetail error: %v", err)
		nc.HandleErrCode(errcode.E.Collection.Create.WithErr(err))
		return
	}
	nc.HandleBusinessSuccess()
}

func GetCollectionForQuery(req common.QueryCollectionExactReq, nowTime time.Time, queryCollection *model.CollectionDetail) (subCollections []*model.CollectionDetail, mainCollectionSize, mainCollectionTotalNum int, err error) {
	var mainCollections []*model.CollectionDetail
	//查询总集合
	if len(req.CollectionIndex) == 0 && len(req.CollectionName) == 0 && req.CollectionStatus == util.DefaultCollectionStatus {
		mainCollections, mainCollectionSize, mainCollectionTotalNum, err = service.GetMainCollectionWithPage(req.UserId, req.Page, req.PageSize)
		if err != nil {
			logger.Error("GetCollectionDetailsWithPage error: %v, userId: %v", err, req.UserId)
			return nil, util.InvalidNumber, util.InvalidNumber, err
		}
	} else {
		mainCollections, mainCollectionSize, mainCollectionTotalNum, err = service.GetMainCollectionWithPageAndTime(queryCollection, req.Page, req.PageSize, nowTime)
		if err != nil {
			logger.Error("GetCollectionDetailByUserIdAndCollection error: %v, userId: %v, collectionIndex: %v", err, req.UserId, req.CollectionIndex)
			return nil, util.InvalidNumber, util.InvalidNumber, err
		}
	}
	//以总集合查询所包含的子集合
	subCollections, err = service.GetSubCollectionByCollections(req.UserId, mainCollections)
	if err != nil {
		logger.Error("GetSubCollectionByCollections error: %v,", err)
		return nil, util.InvalidNumber, util.InvalidNumber, err
	}
	return subCollections, mainCollectionSize, mainCollectionTotalNum, err
}

// QueryCollectionDetail 查询指定的资产集合信息表
func (nc *CollectionDetailController) QueryCollectionDetail() {
	req := common.QueryCollectionExactReq{}
	userId := cast.ToInt(nc.Ctx.Input.Query("userId"))
	if err := nc.CheckUserIdFromSession(userId); err != nil {
		logger.Error(err.Error())
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("无效用户ID"))
		return
	}
	req.UserId = userId
	req.CollectionIndex = nc.Ctx.Input.Query("collectionIndex")
	req.CollectionName = nc.Ctx.Input.Query("collectionName")
	req.CollectionStatus = cast.ToInt(nc.Ctx.Input.Query("collectionStatus"))
	req.Page = cast.ToInt(nc.Ctx.Input.Query("page"))
	req.PageSize = cast.ToInt(nc.Ctx.Input.Query("pageSize"))
	if req.Page <= 0 || req.PageSize <= 0 {
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("页面和页码必须大于零"))
		return
	}
	queryCollection := model.CollectionDetail{}
	queryCollection.UserId = req.UserId
	queryCollection.CollectionIndex = req.CollectionIndex
	queryCollection.CollectionName = req.CollectionName
	queryCollection.CollectionStatus = req.CollectionStatus

	response := &common.CollectionDetailAll{}
	nowTime := time.Now()
	collections, collectionSize, totalNum, err := GetCollectionForQuery(req, nowTime, &queryCollection)
	if err != nil {
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithErr(err))
		return
	}
	response.CollectionSize = collectionSize
	response.TotalNum = totalNum
	if collectionSize == 0 {
		nc.HandleBusinessObj(response)
		return
	}
	err = service.ProcessCollectionForQuery(collections, nowTime, req, response)
	if err != nil {
		logger.Error(err.Error())
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithErr(err))
		return
	}
	nc.HandleBusinessObj(response)
}

//CheckRequiredParameterForModifyCollection 用于在ModifyCollectionDetail中校验必要参数
func (nc *CollectionDetailController) CheckRequiredParameterForModifyCollection(userId int, req common.ModifyCollectionReq) (*model.CollectionDetail, error) {
	if err := nc.CheckUserIdFromSession(req.UserId); err != nil {
		logger.Error(err.Error())
		err = errors.Errorf("无效用户ID")
		return nil, err
	}
	//查询资产集合
	collection, err := service.GetCollectionDetailByUserIdAndCollection(userId, req.CollectionIndex, req.SubCollectionIndex)
	if err != nil {
		logger.Error("GetCollectionDetailByUserIdAndCollection error: %v", err)
		err = errors.Errorf("该集合不存在")
		return nil, err
	}
	//名称长度校验+重名检测
	collections, err := service.CheckCollectionName(req.CollectionName)
	if len(collections) > 0 {
		err = errors.Errorf("集合名已被使用")
		for _, one := range collections {
			if one.CollectionIndex == req.CollectionIndex && one.SubCollectionIndex == req.SubCollectionIndex && one.CollectionName == req.CollectionName {
				err = nil
			}
		}
		if err != nil {
			//nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage(err.Error()))
			return nil, err
		}
	}
	//铸造中不可修改
	if util.InArray(collection.CollectionStatus, util.CollectionForModify) != true {
		err = errors.Errorf("当前状态不可修改")
		logger.Error(err.Error())
		return nil, err
	}
	return collection, nil
}

// ModifyCollectionDetail 修改指定的资产集合信息表
func (nc *CollectionDetailController) ModifyCollectionDetail() {
	//请求头--put
	req := common.ModifyCollectionReq{}
	if err := json.Unmarshal(nc.Ctx.Input.RequestBody, &req); err != nil {
		logger.Error("ModifyCollectionDetail Unmarshal error: %v", err)
		nc.HandleErrCode(errcode.E.Base.RequestInvalid.WithErr(err))
		return
	}
	collection, err := nc.CheckRequiredParameterForModifyCollection(req.UserId, req)
	if err != nil || collection == nil {
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithErr(err))
		return
	}
	if req.SubCollectionIndex == util.DefaultSubCollectionIndex {
		collection.CollectionName = req.CollectionName
		if len(collection.CollectionName) != 0 && len(collection.CollectionIndex) != 0 {
			_, err = service.SetCollectionName(collection.CollectionIndex, collection.CollectionName, util.DefaultCollectionSymbol)
			if err != nil {
				logger.Error("SetCollectionName error: %v", err)
				nc.HandleErrCode(errcode.E.Collection.Query.WithErr(err))
				return
			}
		}
	}
	modify := common.ModifyCollectionDetail{UserId: req.UserId, CollectionIndex: req.CollectionIndex, SubCollectionIndex: req.SubCollectionIndex, CollectionName: &req.CollectionName, QueryCollectionStatus: util.CollectionForModify}
	//检查相关参数
	//校验价格
	err = service.CheckOptionalParameterForModification(collection, req, &modify)
	if err != nil {
		nc.HandleErrCode(errcode.E.Base.RequestInvalid.WithErr(err))
		return
	}
	if req.SubCollectionIndex != util.DefaultSubCollectionIndex {
		err = service.UpdateCollectionDetail(collection)
	} else {
		//查询资产集合
		err = service.UpdateAllSubCollection(&modify)
	}
	if err != nil {
		logger.Error("UpdateCollectionDetail error: %v", err)
		nc.HandleErrCode(errcode.E.Collection.Query.WithErr(err))
		return
	}
	nc.HandleBusinessSuccess()
}

// GetCollectionNamesForMint  获取管理员创建的可用于铸造的集合名字
func (nc *CollectionDetailController) GetCollectionNamesForMint() {
	//获取管理员ID
	adminId, err := service.GetAdminId()
	if err != nil {
		logger.Error("GetCollectionNamesForMint error: %v", err)
		nc.HandleErrCode(errcode.E.Collection.Query.WithErr(err))
		return
	}
	//仅未铸造的总集合可以进行铸造
	collections, err := service.GetCollectionNameForMint(adminId, util.CollectionUnMint)
	if err != nil {
		logger.Error("GetCollectionNameForMint error: %v", err)
		nc.HandleErrCode(errcode.E.Collection.Query.WithErr(err))
		return
	}
	//仅未铸造的集合可以进行铸造
	response := common.CollectionNames{}
	for _, eachCollection := range collections {
		oneCollection := common.CollectionNameForShow{CollectionIndex: eachCollection.CollectionIndex, CollectionName: eachCollection.CollectionName}
		response.Collection = append(response.Collection, oneCollection)
	}
	response.CollectionSize = len(collections)

	nc.HandleBusinessObj(response)
}

//CheckParameterForGetCollectionForPublish 为了函数GetCollectionForPublish检查参数
func (nc *CollectionDetailController) CheckParameterForGetCollectionForPublish(collectionIndex string, subCollectionIndex int) (collection *model.CollectionDetail, userId int, err error) {
	if len(collectionIndex) <= 0 {
		err = errors.Errorf("集合ID为空")
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage(err.Error()))
		return nil, util.InvalidNumber, err
	}
	if subCollectionIndex < 0 {
		err = errors.Errorf("子集合ID无效")
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage(err.Error()))
		return nil, util.InvalidNumber, err
	}
	adminId, err := service.GetAdminId()
	if err != nil {
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("管理员ID为空"))
		return nil, util.InvalidNumber, err
	}
	userId = adminId
	collection, err = service.GetCollectionDetailByUserIdAndCollection(userId, collectionIndex, subCollectionIndex)
	if err != nil {
		logger.Error("GetCollectionForPublish error: %v", err)
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("不存在该集合"))
		return nil, util.InvalidNumber, err
	}
	return collection, userId, nil
}

//GetCollectionForPublish 获取发布页详情
func (nc *CollectionDetailController) GetCollectionForPublish() {
	collectionIndex := nc.Ctx.Input.Query("collectionIndex")
	subCollectionIndex := cast.ToInt(nc.Ctx.Input.Query("subCollectionIndex"))
	collection, userId, err := nc.CheckParameterForGetCollectionForPublish(collectionIndex, subCollectionIndex)
	if err != nil || collection == nil || userId == util.InvalidNumber {
		return
	}
	response := common.CollectionDetailPublishResponse{}
	if subCollectionIndex == util.DefaultSubCollectionIndex {
		subCollection, err := service.GetSubCollectionByStatus(userId, collectionIndex, util.CollectionForModify)
		var subIndex []int
		var lastSubCollection []*model.CollectionDetail
		for _, subId := range subCollection {
			if subId.SubCollectionIndex == util.DefaultSubCollectionIndex {
				continue
			}
			lastSubCollection = append(lastSubCollection, subId)
			subIndex = append(subIndex, subId.SubCollectionIndex)
		}
		metaData, size, err := service.GetMetaDataWithSubCollectionIndex(userId, collectionIndex, subIndex)
		if err != nil || (subIndex != nil && len(subIndex) != size) {
			logger.Error("GetCollectionForPublish error: %v", err)
			nc.HandleErrCode(errcode.E.Internal.Internal.WithErr(err))
			return
		}
		for index := 0; index < size && lastSubCollection != nil; index++ {
			checkStatus := service.CheckCollectionStatus(lastSubCollection[index])
			if metaData[index].SubCollectionIndex == util.DefaultSubCollectionIndex || util.InArray(checkStatus, util.CollectionForModify) == false {
				continue
			}
			err = service.BuildResponseForPublish(&response, collectionIndex, metaData[index], lastSubCollection[index])
			if err != nil {
				nc.HandleErrCode(errcode.E.Internal.Internal.WithErr(err))
				return
			}
		}
	} else {
		metaData, _, err := service.GetMetaDataWithSubCollectionIndex(userId, collectionIndex, []int{subCollectionIndex})
		if err != nil {
			logger.Error("GetCollectionForPublish error: %v", err)
			nc.HandleErrCode(errcode.E.Internal.Internal.WithErr(err))
			return
		}
		if err = service.BuildResponseForPublish(&response, collectionIndex, metaData[0], collection); err != nil {
			nc.HandleErrCode(errcode.E.Internal.Internal.WithErr(err))
			return
		}
	}
	response.Size = len(response.CollectionsInfo)
	nc.HandleBusinessObj(response)
}

//PublishCollection 发布集合
func (nc *CollectionDetailController) PublishCollection() {
	req := common.PublishCollectionReq{}
	if err := json.Unmarshal(nc.Ctx.Input.RequestBody, &req); err != nil {
		logger.Error("PublishCollection Unmarshal error: %v", err)
		nc.HandleErrCode(errcode.E.Base.RequestInvalid.WithErr(err))
		return
	}
	if err := nc.CheckUserIdFromSession(req.UserId); err != nil {
		logger.Error(err.Error())
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("无效用户ID"))
		return
	}
	userId := cast.ToInt(nc.GetSession(common.SessionUserId))
	//查询资产集合
	collection, err := service.GetCollectionDetailByUserIdAndCollection(userId, req.CollectionIndex, req.SubCollectionIndex)
	if err != nil {
		logger.Error("GetCollectionDetailByUserIdAndCollection error: %v", err)
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("查找集合不存在"))
		return
	}
	if err = service.ProcessInPublishCollection(userId, collection, req); err != nil {
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage(err.Error()))
		return
	}
	nc.HandleBusinessSuccess()
}

//GetCollectionInfoForHomePage 获取首页集合信息
func (nc *CollectionDetailController) GetCollectionInfoForHomePage() {
	req := common.CollectionHomePageReq{}
	req.Page = cast.ToInt(nc.Ctx.Input.Query("page"))
	req.PageSize = cast.ToInt(nc.Ctx.Input.Query("pageSize"))
	req.CollectionStatus = cast.ToInt(nc.Ctx.Input.Query("collectionStatus"))

	if req.Page <= 0 || req.PageSize <= 0 {
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("页码和页面大小必须大于零"))
		return
	}
	adminId, err := service.GetAdminId()
	userId := adminId
	if err != nil {
		logger.Error("GetCollectionNames error: %v", err)
		nc.HandleErrCode(errcode.E.Collection.Query.WithErr(err))
		return
	}
	//获取管理下指定可见性状态的集合
	collections, size, totalNum, err := service.GetCollectionForHomePage(userId, req.CollectionStatus, time.Now(), req.Page, req.PageSize)
	if err != nil {
		logger.Error("GetCollectionNames error: %v", err)
		nc.HandleErrCode(errcode.E.Collection.Query.WithErr(err))
		return
	}
	response := common.CollectionDetailHomePage{CollectionSize: size, TotalNum: totalNum}
	if size == 0 {
		nc.HandleBusinessObj(response)
		return
	}
	queryMetadata, err := service.GetMetaDataFromCollectionsInOrder(userId, size, collections)
	if err != nil {
		nc.HandleErrCode(errcode.E.Collection.Query.WithErr(err))
		return
	}
	//遍历二维数组，重新组合数据
	err = service.RebuildCollectionAndMetaData(&response, collections, size, queryMetadata, req.CollectionStatus)
	if err != nil {
		logger.Error("RebuildCollectionAndMetaData error: %v", err)
		nc.HandleErrCode(errcode.E.Collection.Query.WithErr(err))
		return
	}
	nc.HandleBusinessObj(response)
}

func (nc *CollectionDetailController) GetCollectionInfoForRecommend() {
	req := common.CollectionRecommendReq{}
	req.Num = cast.ToInt(nc.Ctx.Input.Query("num"))
	if req.Num <= 0 {
		nc.HandleErrCode(errcode.E.Base.ErrorMessage.WithMessage("推荐列表请求错误，请求数量不能小于零"))
		return
	}
	adminId, err := service.GetAdminId()
	userId := adminId
	if err != nil {
		logger.Error("GetAdminId error: %v", err)
		nc.HandleErrCode(errcode.E.Collection.Query.WithErr(err))
		return
	}
	//获取管理下指定可见性状态的集合
	nowTime := time.Now()
	collections, size, totalNum, err := service.GetLastCollectionTimeWithStatus(userId, util.CollectionStatusForHomePage, nowTime, req.Num)
	if err != nil {
		logger.Error("GetCollectionNames error: %v", err)
		nc.HandleErrCode(errcode.E.Collection.Query.WithErr(err))
		return
	}
	response := common.CollectionDetailRecommend{CollectionSize: size, TotalNum: totalNum}
	if size == 0 {
		nc.HandleBusinessObj(response)
		return
	}
	queryMetadata, err := service.GetMetaDataFromCollectionsInOrder(userId, size, collections)
	if err != nil {
		nc.HandleErrCode(errcode.E.Collection.Query.WithErr(err))
		return
	}
	err = service.RebuildCollectionAndMetaDataForRecommend(&response, collections, size, queryMetadata)
	if err != nil {
		logger.Error("CollectionDetailDescription Unmarshal error: %v", err)
		nc.HandleErrCode(errcode.E.Collection.Query.WithErr(err))
		return
	}
	nc.HandleBusinessObj(response)
}
